package com.ctbri.concurrency;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

class Meal {
	private final int orderNum;
	public Meal(int orderNum) {
		this.orderNum = orderNum;
	}
	public String toString() {
		return "Meal " + orderNum;
	}
}

class WaitPerson implements Runnable {
	private Restaurant restaurant;
	public WaitPerson(Restaurant r) {
		restaurant = r;
	}
	public void run() {
		try {
			while(!Thread.interrupted()) {
				synchronized(this) {
					while(restaurant.meal == null)
						wait();
				}
				System.out.println("Waitpersion got " + restaurant.meal);
				Meal tmp;
				synchronized(restaurant.chef){
					tmp = restaurant.meal;
					restaurant.meal = null;
					restaurant.chef.notifyAll();
				}
				synchronized(restaurant.busyBoy) {
					restaurant.toClean = tmp;
					restaurant.busyBoy.notifyAll();
				}
			}
		} catch (InterruptedException e) {
			System.out.println("WaitPerson interrupted");
		}
	}
}

class Chef implements Runnable {
	private Restaurant restaurant;
	private int count = 0;
	public Chef (Restaurant r) {
		restaurant = r;
	}
	public void run() {
		try{
			while(!Thread.interrupted()) {
				synchronized(this) {
					while(restaurant.meal != null)
						wait();
				}
				if(++count == 10) {
					System.out.println("Out of food, closing");
					restaurant.exec.shutdownNow();
					return;
				}
				System.out.print("Order up!");
				synchronized(restaurant.waitPerson) {
					restaurant.meal = new Meal(count);
					restaurant.waitPerson.notifyAll();
				}
				TimeUnit.MILLISECONDS.sleep(100);
			}
		} catch (InterruptedException e) {
			System.out.println("Chef interrupted");
		}
	}
}

class BusyBoy implements Runnable {
	private Restaurant restaurant;
	public BusyBoy(Restaurant restaurant) {
		this.restaurant = restaurant;
	}
	public void run() {
		try{
			while(!Thread.interrupted()) {
				synchronized(this) {
					while(restaurant.toClean == null)
						wait();
				}
				System.out.println("Clean up!");
				restaurant.toClean = null;
				
			}
		} catch(InterruptedException e) {
			System.out.println("BusyBoy interrupted");
		}
	}
}

public class Restaurant {
	Meal meal;
	Meal toClean;
	ExecutorService exec = Executors.newCachedThreadPool();
	WaitPerson waitPerson = new WaitPerson(this);
	BusyBoy busyBoy = new BusyBoy(this);
	
	Chef chef = new Chef(this);
	public Restaurant() {
		exec.execute(chef);
		exec.execute(waitPerson);
		exec.execute(busyBoy);
	}
	
	public static void main(String[] args) {
		new Restaurant();
	}

}
